package io.renren.service;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.WriteTable;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import io.renren.dao.GeneratorDao;
import io.renren.entity.DataChecklist;
import io.renren.entity.DocColumnEntity;
import io.renren.entity.DocTableEntity;
import io.renren.utils.ConfigUtils;
import io.renren.utils.FileUtils;
import io.renren.utils.RRException;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.velocity.VelocityContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 文档生成器
 *
 * @author huangjiang <huangjiang1026@gmail.com>
 * @date 2020-02-25 17:51
 */
@Service
public class FileGeneratorService {

    @Autowired
    private GeneratorDao generatorDao;

    /**
     * 生成文件
     *
     * @param tableNames 表名数组
     */
    public byte[] generatorFile(List<String> tableNames) {
        // 获取表数据
        List<DocTableEntity> tableEntities = generatorTables(tableNames);
        if (tableEntities.isEmpty()) {
            throw new RRException("获取表数据失败");
        }

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);

        // 生成Word版的数据库设计说明书模板
        generateWordOfDesignNodes(tableEntities, zip);

        // 生成Excel版的数据库设计说明书模板
        generateExcelOfDesignNodes(tableEntities, zip);

        IOUtils.closeQuietly(zip);
        return outputStream.toByteArray();
    }

    /**
     * 生成Word版的数据库设计说明书模板
     *
     * @param tableEntities 表数据
     * @param zip           zip输出流
     */
    public void generateWordOfDesignNodes(List<DocTableEntity> tableEntities, ZipOutputStream zip) {
        // 设置velocity资源加载器
        FileUtils.resourceLoader();

        // 封装模板数据
        Map<String, Object> map = new HashMap<>(1);
        map.put("tables", tableEntities);

        // 生成Word版的数据库设计说明书模板  A模板
        generateWordTemplate(map, "word/DesignNodesA.xml.vm", "DesignNodesA.xml", zip);

        // 生成Word版的数据库设计说明书模板  B模板
        generateWordTemplate(map, "word/DesignNodesB.xml.vm", "DesignNodesB.xml", zip);
    }

    /**
     * 生成Word版的数据库设计说明书模板
     *
     * @param map      表数据
     * @param template 模板
     * @param fileName 文件名
     * @param zip      zip输出流
     */
    private void generateWordTemplate(Map<String, Object> map, String template, String fileName, ZipOutputStream zip) {
        String errorMsg = "渲染模板失败";
        FileUtils.templateFileWriteToZip(zip, new VelocityContext(map), template, fileName, errorMsg);
    }

    /**
     * 生成Excel版的数据库设计说明书模板
     *
     * @param tableEntities 表数据
     * @param zip           zip输出流
     */
    public void generateExcelOfDesignNodes(List<DocTableEntity> tableEntities, ZipOutputStream zip) {
        // 生成Excel版的数据库设计说明书模板  DesignNodes
        generateDesignNodes(tableEntities, zip);
        // 生成Excel版的数据库设计说明书模板  DataChecklist
        generateDataChecklist(tableEntities, zip);
    }

    /**
     * 生成Excel版的数据库设计说明书模板  DesignNodes
     *
     * @param tableEntities 表数据
     * @param zip           zip输出流
     */
    private void generateDesignNodes(List<DocTableEntity> tableEntities, ZipOutputStream zip) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        ExcelWriter excelWriter = EasyExcel.write(baos)
                .excelType(ExcelTypeEnum.XLSX)
                .registerWriteHandler(horizontalCellStyleStrategy(IndexedColors.BRIGHT_GREEN))
                .build();

        int sheetNo = 0;
        for (DocTableEntity tableEntity : tableEntities) {
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, tableEntity.getTableName())
                    .needHead(Boolean.FALSE)
                    .build();
            // 由于生成的数据库设计说明书模板第一行是表名+表描述，所以采用如下方式实现：
            // 这里必须指定需要头，table 会继承sheet的配置，sheet配置了不需要，table 默认也是不需要
            // 表1
            WriteTable writeTable1 = EasyExcel.writerTable(0).build();
            // 处理表1的数据，表1数据是自定义的
            List<List<String>> table1headers = new ArrayList<>();
            List<String> tableInfo = new ArrayList<>();
            tableInfo.add("  " + tableEntity.getTableComment() + "  " + tableEntity.getTableName());
            table1headers.add(tableInfo);
            excelWriter.write(table1headers, writeSheet, writeTable1);

            // 表2
            WriteTable writeTable2 = EasyExcel.writerTable(1)
                    .needHead(Boolean.TRUE)
                    .head(DocColumnEntity.class)
                    .build();
            // 处理表2的数据
            excelWriter.write(tableEntity.getColumns(), writeSheet, writeTable2);

            sheetNo++;
        }
        // 关闭流
        excelWriter.finish();

        try {
            zip.putNextEntry(new ZipEntry("DesignNodes.xlsx"));
            IOUtils.write(baos.toByteArray(), zip);
            IOUtils.closeQuietly(baos);
            zip.closeEntry();
        } catch (Exception e) {
            throw new RRException("生成Excel模板失败", e);
        }
    }

    /**
     * 生成Excel版的数据库设计说明书模板  DataChecklist
     *
     * @param tableEntities 表数据
     * @param zip           zip输出流
     */
    private void generateDataChecklist(List<DocTableEntity> tableEntities, ZipOutputStream zip) {
        // 组装对象集合
        List<DataChecklist> dataChecklists = new ArrayList<>();
        tableEntities.forEach(tableEntity -> {
            tableEntity.getColumns().forEach(columnEntity -> {
                DataChecklist dataChecklist = new DataChecklist();
                BeanUtils.copyProperties(columnEntity, dataChecklist);
                dataChecklist.setTableComment(tableEntity.getTableComment());
                dataChecklist.setTableName(tableEntity.getTableName());

                dataChecklists.add(dataChecklist);
            });
        });

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        ExcelWriter excelWriter = EasyExcel.write(baos, DataChecklist.class)
                .excelType(ExcelTypeEnum.XLSX)
                .needHead(Boolean.TRUE)
                .registerWriteHandler(horizontalCellStyleStrategy(IndexedColors.LIGHT_ORANGE))
                .build();
        WriteSheet writeSheet = EasyExcel.writerSheet("DataChecklist").build();
        excelWriter.write(dataChecklists, writeSheet);
        // 关闭流
        excelWriter.finish();

        try {
            zip.putNextEntry(new ZipEntry("DataChecklist.xlsx"));
            IOUtils.write(baos.toByteArray(), zip);
            IOUtils.closeQuietly(baos);
            zip.closeEntry();
        } catch (Exception e) {
            throw new RRException("生成Excel模板失败", e);
        }
    }

    /**
     * 定义行样式策略
     *
     * @param colors 颜色值索引
     * @return WriteHandler
     */
    private WriteHandler horizontalCellStyleStrategy(IndexedColors colors) {
        // 表头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 背景设置为绿色
        headWriteCellStyle.setFillForegroundColor(colors.getIndex());
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 12);
        headWriteFont.setBold(true);
        headWriteCellStyle.setWriteFont(headWriteFont);
        headWriteCellStyle.setBorderTop(BorderStyle.THIN);
        headWriteCellStyle.setBorderRight(BorderStyle.THIN);
        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);

        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        WriteFont contentWriteFont = new WriteFont();
        // 字体大小
        contentWriteFont.setFontHeightInPoints((short) 12);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);

        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

    /**
     * 生成表实体数据
     *
     * @param tableNames 表名数组
     * @return List<DocTableEntity>
     */
    private List<DocTableEntity> generatorTables(List<String> tableNames) {
        // 配置信息
        Configuration config = ConfigUtils.getConfig();

        List<DocTableEntity> tableEntities = new ArrayList<>();
        // 循环表
        generatorDao.queryDocTableList(tableNames).forEach(tableEntity -> {
            // 获取表所有的列
            List<DocColumnEntity> columnEntities = generatorDao.queryDocColumnList(tableEntity.getTableName());
            if (null != columnEntities && !columnEntities.isEmpty()) {
                // 循环列
                columnEntities.forEach(columnEntity -> {
                    // 列数字精度
                    Long numericPrecision = columnEntity.getNumericPrecision();
                    // 列小数点位数
                    Long numericScale = columnEntity.getNumericScale();
                    // 列字符长度
                    String columnLength = StringUtils.defaultIfBlank(columnEntity.getColumnLength(), "");
                    if (null != numericPrecision && null != numericScale && numericScale > 0) {
                        columnLength = numericPrecision.toString() + "," + numericScale.toString();
                    }
                    // 列长度
                    columnEntity.setColumnLength(columnLength);

                    // 列主键
                    String columnKey = columnEntity.getColumnKey();
                    columnEntity.setColumnKey("PRI".equalsIgnoreCase(columnKey) ? config.getString("columnKeyYes") : config.getString("columnKeyNo"));

                    // 列是否允许空
                    String columnNullable = columnEntity.getColumnNullable();
                    columnEntity.setColumnNullable("YES".equalsIgnoreCase(columnNullable) ? config.getString("columnNullableYes") : config.getString("columnNullableNo"));

                    // 列默认值
                    columnEntity.setColumnDefault(StringUtils.defaultIfBlank(columnEntity.getColumnDefault(), ""));

                    // 列补充内容
                    String extra = columnEntity.getExtra();
                    columnEntity.setExtra("auto_increment".equalsIgnoreCase(extra) ? config.getString("columnAutoIncrementYes") : config.getString("columnAutoIncrementNo"));

                    // 列注释
                    columnEntity.setColumnComment(StringUtils.defaultIfBlank(columnEntity.getColumnComment(), ""));

                });

                tableEntity.setTableComment(StringUtils.defaultIfBlank(tableEntity.getTableComment(), ""));
                tableEntity.setColumns(columnEntities);
                tableEntities.add(tableEntity);
            }
        });

        return tableEntities;
    }

}
